using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Ltf.Diffs
{

	public class Diff2Document : DiffDocument
	{


		public Diff2Document(string fileName)
			: base (fileName)	{	}

		public Diff2Document(string fileName, int changeSet, string author)
			: base(fileName, changeSet, author) {}
		


		public void SetDiff(Diff2Results results, DiffSide side)
		{
			switch (side)
			{
				case DiffSide.Left:
					SetDiffLeft(results);
					break;
				case DiffSide.Right:
					SetDiffRight(results);
					break;
				case DiffSide.Combined:
					SetDiffCombined(results);
					break;
				default:
					break;
			}
		}


		private void SetDiffLeft(Diff2Results results)
		{
			int blankLineOffset = 0;
			foreach (Diff2Results.DiffHunk hunk in results.Hunks)
			{
				switch (hunk.HunkType)
				{
					case Diff2Results.DiffHunkType.Change:
						for (int line = hunk.SourceRange.From; line <= hunk.SourceRange.To; line++)
							Lines[line + blankLineOffset - 1].SetLineState(LineState.ChangedFrom);
						if (hunk.TargetRange.Interval > hunk.SourceRange.Interval)
						{
							for (int i = 0; i < hunk.TargetRange.Interval - hunk.SourceRange.Interval; i++)
							{
								Lines.Insert(hunk.SourceRange.To + blankLineOffset, new Line(LineState.ChangedFrom));
								blankLineOffset++;
							}
						}
						break;
					case Diff2Results.DiffHunkType.Added:
						for (int line = hunk.TargetRange.From; line <= hunk.TargetRange.To; line++)
							Lines.Insert(hunk.SourceRange.From + blankLineOffset, new Line(LineState.Inserted));
						blankLineOffset += hunk.TargetRange.Interval;
						break;
					case Diff2Results.DiffHunkType.Deleted:
						for (int line = hunk.SourceRange.From; line <= hunk.SourceRange.To; line++)
							Lines[line + blankLineOffset - 1].SetLineState(LineState.Deleted);
						break;
					default:
						break;
				}
			}

		}

		private void SetDiffRight(Diff2Results results)
		{
			int blankLineOffset = 0;
			foreach (Diff2Results.DiffHunk hunk in results.Hunks)
			{
				switch (hunk.HunkType)
				{
					case Diff2Results.DiffHunkType.Change:
						for (int line = hunk.TargetRange.From; line <= hunk.TargetRange.To; line++)
							Lines[line + blankLineOffset - 1].SetLineState(LineState.ChangedTo);
						if (hunk.SourceRange.Interval > hunk.TargetRange.Interval)
						{
							for (int i = 0; i < hunk.SourceRange.Interval - hunk.TargetRange.Interval; i++)
							{
								Lines.Insert(hunk.TargetRange.To + blankLineOffset, new Line(LineState.ChangedTo));
								blankLineOffset++;
							}
						}
						break;
					case Diff2Results.DiffHunkType.Added:
						for (int line = hunk.TargetRange.From; line <= hunk.TargetRange.To; line++)
							Lines[line + blankLineOffset - 1].SetLineState(LineState.Inserted);
						break;
					case Diff2Results.DiffHunkType.Deleted:
						for (int line = hunk.SourceRange.From; line <= hunk.SourceRange.To; line++)
							Lines.Insert(hunk.TargetRange.From + blankLineOffset, new Line(LineState.Deleted));
						blankLineOffset += hunk.SourceRange.Interval;
						break;
					default:
						break;
				}
			}

		}

		private void SetDiffCombined(Diff2Results results)
		{
			int blankLineOffset = 0;
			foreach (Diff2Results.DiffHunk hunk in results.Hunks)
			{
				switch (hunk.HunkType)
				{
					case Diff2Results.DiffHunkType.Change:
						for (int line = hunk.SourceRange.From; line <= hunk.SourceRange.To; line++)
						{
							Line builtline = new Line(LineState.ChangedFrom, hunk.SourceLines[line - hunk.SourceRange.From]);
							Lines.Insert(hunk.TargetRange.From + blankLineOffset - 1, builtline);
							blankLineOffset++;
						}
						for (int line = hunk.TargetRange.From; line <= hunk.TargetRange.To; line++)
							Lines[line + blankLineOffset - 1].SetLineState(LineState.ChangedTo);
						break;
					case Diff2Results.DiffHunkType.Added:
						for (int line = hunk.TargetRange.From; line <= hunk.TargetRange.To; line++)
							Lines[line + blankLineOffset - 1].SetLineState(LineState.Inserted);
						break;
					case Diff2Results.DiffHunkType.Deleted:
						for (int line = hunk.SourceRange.From; line <= hunk.SourceRange.To; line++)
							Lines.Insert(hunk.TargetRange.From + blankLineOffset, new Line(LineState.Deleted, hunk.SourceLines[line - hunk.SourceRange.From]));
						blankLineOffset += hunk.SourceRange.Interval;
						break;
					default:
						break;
				}
			}

		}

		private Dictionary<int, int> blameLineMap = null;
		public void AddBlameResults(Diff2Results results, int changeSet, string author)
		{
			if (blameLineMap == null)
			{
				blameLineMap = new Dictionary<int, int>();

				for (int i = 0; i < Lines.Count; i++)
				{
					blameLineMap.Add(i, i);
				}
			}

			blameLineMap = AddBlameResults(results, blameLineMap, changeSet, author);
		}

		private Dictionary<int, int> AddBlameResults(Diff2Results results, Dictionary<int, int> lineMap, int changeSet, string author)
		{
			int sourcePointer = 0;
			int targetPointer = 0;
			Dictionary<int, int> newLineMap = new Dictionary<int, int>();
			foreach (Diff2Results.DiffHunk hunk in results.Hunks)
			{
				while (targetPointer + 1 < hunk.TargetRange.From)
				{
					if (lineMap.ContainsKey(targetPointer))
					{
						newLineMap.Add(sourcePointer, lineMap[targetPointer]);
					}
					sourcePointer++;
					targetPointer++;
				}

				switch (hunk.HunkType)
				{
					case Diff2Results.DiffHunkType.Change:
						for (int line = hunk.TargetRange.From - 1; line < hunk.TargetRange.To; line++)
						{
							if (lineMap.ContainsKey(line))
								Lines[lineMap[line]].SetLineProperties(changeSet == 0 ? LineState.NotCheckedIn : LineState.ChangedTo, changeSet, author);
						}
						sourcePointer += hunk.SourceRange.Interval;
						targetPointer += hunk.TargetRange.Interval;
						break;
					case Diff2Results.DiffHunkType.Added:
						for (int line = hunk.TargetRange.From - 1; line < hunk.TargetRange.To; line++)
						{
							if (lineMap.ContainsKey(line))
								Lines[lineMap[line]].SetLineProperties(changeSet == 0 ? LineState.NotCheckedIn : LineState.Inserted, changeSet, author);
						}
						targetPointer += hunk.TargetRange.Interval;
						break;
					case Diff2Results.DiffHunkType.Deleted:
						sourcePointer += hunk.SourceRange.Interval;
						break;
					default:
						break;
				}
			}
			while (targetPointer < Lines.Count)
			{
				if (lineMap.ContainsKey(targetPointer))
				{
					newLineMap.Add(sourcePointer, lineMap[targetPointer]);
				}
				sourcePointer++;
				targetPointer++;
			}
			return newLineMap;
		}



		public override void LoadResults(Diff3Results results)
		{
			throw new Exception("The method or operation is not implemented.");
		}
	}

	
}
